# -*- coding: utf-8 -*-

"""
Module implementing MainWindow.
"""
import re
import codecs

from PyQt4.Qt import Qt
from PyQt4.QtGui import QMainWindow, QPalette, QColor, QIcon,  QBrush, QComboBox, QMovie, QLabel, QListView, QItemSelectionModel, QMessageBox
from PyQt4.QtCore import pyqtSignature, QUrl,  QTimer
from PyQt4.QtWebKit import QWebPage
import urllib2

from Ui_mainwindow import Ui_MainWindow
from captchadialog import CaptchaDialog
import apina
from listwidget import OmaListWidgetItem, LatausListWidgetItem


class MainWindow(QMainWindow, Ui_MainWindow):
    """
    Class documentation goes here.
    """
    def __init__(self, parent = None):
        """
        Constructor
        """
        QMainWindow.__init__(self, parent)
        self.setupUi(self)
        # delegoi linkin klikkaukset mainwindow:n kautta???
        self.sivunakyma3.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
        self.sivunakyma3.linkClicked.connect(self.on_sivunakyma3_linkClicked)
        self.tuloslista.currentItemChanged.connect(self.on_tuloslista_currentItemChanged)
        self.koodi_muuttunut = 0
        self.wiki = None
        # Current page under editing.
        self.currentRev = None
        self.currentTitle = None
        self.currentTimestamp = None
        # Listasta valittu alkio.
        self.valittuOli = None
        self.editing = -1
        self.summary_list_file = "./summary_list.txt"
        self.load_summary_list()
        self.muutoskommentti.setInsertPolicy(QComboBox.InsertAtTop)

        # Lasketaan yritykset, jos tallennus ei onnistu verkkosyistä.
        self.tallennusyrityksia = 0

        self.automaatti.setCheckable(True)
        # Näyttää tekstin SAMA tai ERI, sen mukaan onko tekstilaatikoiden teksti sama vai eri
        self.statusSama = QLabel(self)
        self.statusBar().addPermanentWidget(self.statusSama)
        
        # widgetti latausanimaation näyttämiseen
        self.statusBar().addPermanentWidget(self.tee_animaatiolbl())
        # Latausanimaatio kun ladataan tavaraa webistä.

    def tee_animaatiolbl(self):
        # GIF:n näyttävä labeli
        lbl = QLabel(self)

        # Load animated GIF
        self.movie = QMovie("resources/latausanimaatio.gif");

        #Make sure the GIF was loaded correctly
        if not self.movie.isValid(): 
            print "MainWindow: Animaatiota ei voitu ladata."
        lbl.setMovie(self.movie);
        return lbl

    def set_mode(self, mode):
        """
        Show login name in the label.
        """
        if mode == "edit":
            self.rbEditText.setChecked(True)
        elif mode == "rename":
            self.rbRename.setChecked(True)
        elif mode == "undo":
            self.rbUndo.setChecked(True)

    def set_login(self,  name):
        """
        Show login name in the label.
        """
        self.loginLabel.setText(name)
        self.loginLabel.setTextFormat(1) # ???
        
        
    def set_list(self, lsto):
        """
        Laittaa titlelistan `lsto` sivulistaan ja lista-jäsenmuuttujaan.
        
        :param lsto (ArtLista) 
        """
        if lsto:
            self.lista = lsto
            self.tuloslista.clear()

            # Jos lista on tyyppiä, jossa sivuja ladataan apin kautta osissa, lisätään
            # jatkonappula listan perään, joka valitsemalla ladataan lisää sivuja.
            if self.lista.is_continuable():
                np = self.lista.get_next_part()
                while len(np) == 0 and self.lista.has_more():
                    np = self.lista.get_next_part()
                    QTimer.singleShot(10000, lambda: self.set_list(lsto))

                for a in np: self.tuloslista.addItem(OmaListWidgetItem(a))

                if self.lista.has_more():
                    jatko = LatausListWidgetItem()
                    self.tuloslista.addItem(jatko)
            else:
#                self.tuloslista.addItems(self.lista.get_titlelist())
                for a in self.lista.get_titlelist(): self.tuloslista.addItem(OmaListWidgetItem(a))

        self.set_mode(lsto.get_mode())


    def list_more(self):
        if self.lista.is_continuable():

            # Poistetaan jatkonappula, lisätään uudet ja lisätään jatkonappula takaisin ja
            # valitaan jatkonapin tilalle tullut ensimmäinen alkio.
            lst = self.lista.get_next_part()
            vika = self.tuloslista.count()-1
            if lst != []:
                items = map(lambda a: OmaListWidgetItem(a), lst)
                for a in items: self.tuloslista.addItem(a)
                jatko = self.tuloslista.takeItem(vika)
                if self.lista.has_more():
                    self.tuloslista.addItem(jatko)
                    self.tuloslista.scrollToItem(items[0], QListView.PositionAtCenter) 
                    #items[0].setSelected(True)


    def load_summary_list(self):
        """
        Lataa tallennetut summaryt.
        """
        try:
            with codecs.open(self.summary_list_file, "r", encoding="utf-8") as f:
                lines = f.readlines()
                self.muutoskommentti.addItems(map(lambda l: l.strip(), lines))
                print u"LISÄTTY {0} riviä".format(len(lines))
        except IOError as e:
            print u"Summarylistaa ei voitu lukea: " + str(e)

    def save_summary_list(self):
        """
        Tallentaa nykyiset summaryt.
        """
        with codecs.open(self.summary_list_file, "w", "utf-8", "replace") as f:
            for i in xrange(self.muutoskommentti.count()):
                komm = unicode(self.muutoskommentti.itemText(i))
                if komm.strip() != "":
                    f.write(u"{0}\n".format(komm))
            print u"Tallennettu {0} riviä".format(self.muutoskommentti.count())
    
    @pyqtSignature("QUrl")
    def on_sivunakyma3_linkClicked(self, p0):
        """
        Avaa webbinäkymässä klikatun linkin. TODO
        """

        print "linkClicked"
        #raise NotImplementedError
        print unicode(p0.toString())
        if p0.host() == "fi.wiktionary.org":
            kohdesivu = p0.queryItemValue("title")
            if not kohdesivu:
                m = re.search("/wiki/(.*)", p0.path())
                kohdesivu = m.group(1)
                
            print u"kohdesivu: " + kohdesivu
            self.lataa_sivu(kohdesivu)
        else:
            print u"linkki ulos"
        self.sivunakyma3.load(p0)
    
    @pyqtSignature("")
    def on_tallennus_clicked(self):
        """
        Lähettää muutokset palvelimelle.
        """
        def captcha_cb(id,  url):
            """
            Captchaa kyselevä callback.
            """
            text, ok = CaptchaDialog.getText(self, 'Ratkaise captcha', 'Captcha: ',  url)
            if ok:
                return str(text)
            return None
                    
        print u"Tallennus cliked"
        simulate = False
        self.current.wtxt  = self.get_current_text()
        time  = self.currentTimestamp
        comm  = unicode(self.muutoskommentti.currentText())
        minor = self.minorEdit.isChecked()
        self.current.set_summary(comm)

        # Lisätään kommentti comboboxin listaan, jos sitä ei ole.
        if self.muutoskommentti.findText(comm) == -1:
            self.muutoskommentti.addItem(comm)
        self.piilota_animaatio()

        # Lähetetään muutokset palvelimelle
        try:
            if simulate:
                onnistui = True
            elif self.rbEditText.isChecked():
                onnistui = self.lista.edit_page(self.current, time, captcha_cb, minor)
            elif self.rbRename.isChecked():
                onnistui = self.lista.rename_page(self.current, captcha_cb)
            elif self.rbUndo.isChecked():
                self.current.set_summary("Kumottu muokkaus " + self.leUndo.text() + ", jonka teki " + self.lblLastEditor.text())
                onnistui = self.lista.undo_rev(self.current, int(self.leUndo.text()), time, captcha_cb, minor)

            if onnistui:
                self.tuloslista.currentItem().set_savesuccess()
                print u"Tallentaminen onnistui."
                # käynnistetään tallennuksen jälkeinen toiminta
                self.trigger_after_save_action(self.current.title())

        except apina.ApiException as e:
            ret = QMessageBox.critical(self, u"Hunsbot", "Virhe: {}".format(e), \
                                           QMessageBox.Retry | QMessageBox.Abort, QMessageBox.Retry)
            if ret == QMessageBox.Retry:
                self.on_tallennus_clicked()
            else:
                print u"Automaatti pysäytetään"
                # Pysäytetään automaatti.
                self.automaatti.setChecked(False) 

        except urllib2.HTTPError as e:
            self.tallennusyrityksia += 1
            if self.tallennusyrityksia <= 3:
                print "HTTP-virhe yritetään uudelleen"
                QTimer.singleShot(10000, self.timerTallenna_triggered)
            else:
                ret = QMessageBox.critical(self, u"Hunsbot", "3. HTTP-virhe")
        except:
            ret = QMessageBox.critical(self, u"Hunsbot", "Odottamaton virhe")
            raise
        else:
            self.tallennusyrityksia = 0


    def nayta_animaatio(self):
        """
        Käynnistää latausanimaation.
        """
#        print u"animaatio päälle"
        self.movie.start()

    def piilota_animaatio(self):
        """
        Pysäyttää latausanimaation.
        """
#        print u"animaatio pois"
        self.movie.stop()

    def trigger_after_save_action(self, title):
        """
        Käynnistää onnistuneen tallennuksen jälkeen tehtävän toiminnan.

        :param title (unicode) 
        """
        if self.afterSaveAction_reload.isChecked():
            self.lataa_sivu(title)
        elif self.afterSaveAction_next.isChecked():
            self.on_actionSeuraava_triggered()

    def is_automation_on(self):
        return self.automaatti.isChecked()

    @pyqtSignature("")
    def set_automation(self, on):
        """
        Kytkee automaattitallennuksen päälle, jos `on` = True, tai pois päältä, jos False.
        """

        def disable_gui(b):
            self.tallennus.setDisabled(b)
            self.groupBox.setDisabled(b)
            self.koodinakyma2.setReadOnly(b)
            self.tuloslista.setDisabled(b)

        # Päälle.
        if on == True:
            disable_gui(True)
            self.current.paused = False

            # Tallennetaan tieto valitusta, koska automaatissa ladataan aina seuraava.
            options = [self.afterSaveAction_reload, self.afterSaveAction_next, self.afterSaveAction_next]
            self.valittuOli = [x for x in options if lambda e: e.isChecked()][0]

            self.afterSaveAction_next.setChecked(True)
#            self.trigger_after_save_action(self.current.title())
            self.lataa_sivu(self.current.title())  # ?? kokeillaan toimiiko jos ladataan sivu uudelleen

        # Sammutetaan.
        else:
            disable_gui(False)

            # Palautetaan valittu toiminta.
            assert self.valittuOli != None
            self.valittuOli.setChecked(True)

    @pyqtSignature("bool")
    def on_automaatti_toggled(self, on):
        """
        Kytkee automaattitallennuksen päälle.
        """
        print "toggle: {}".format(on)
        self.set_automation(on)


    def lataa_sivu(self, title):
        """
        Lataa uuden sivun.

        :param title (unicode)
        """
        self.nayta_animaatio()

        # pitää ottaa pois päältä, että muutokset näkyy??
        self.merkitse_samaksi(False)
        if title == "" :
            print "Outoa, title = ''"
            self.currentRev = None
            self.currentTitle = None
            self.koodinakyma1.setText("")
            self.koodinakyma2.setText("")
            self.koodinakyma3.setText("")
            return

        print u"haetaan: " + title
        self.currentRev, self.currentTimestamp = self.lista.get_wikitext(title)
        self.currentTitle = title
        if not self.currentRev:
            uusi_sivu = True
            print u"Sivua ei ole"
            #self.tuloslista.currentItem().setIcon(QIcon("resources/ax-key-no.png"))
            self.tuloslista.currentItem().set_newpage() #setBackground(QBrush(QColor("pink")))
            alkup_wtxt = None
        else:
            uusi_sivu = False
            alkup_wtxt = self.currentRev
            self.koodinakyma1.setText(alkup_wtxt)        

        # Editoidaan olemassa olevaa sivua.
        if not uusi_sivu:
            current = self.lista.get_modified(title, alkup_wtxt) 
            self.lblLastEdited.setText(current.parent.last_edited())
            self.lblLastEditor.setText(current.parent.last_editor())
            if self.rbEditText.isChecked():
                if current.action.has_text():
                    current.wtxt = unicode(current.action.text)
                    self.koodinakyma2.setText(current.action.text)
                else:
                    self.koodinakyma2.setText(alkup_wtxt)

            elif current.action.has_undo() and self.rbUndo.isChecked():
                self.koodinakyma2.setText(u"<i>[Kumotaan revisio {}]</i>".format(current.action.rev_id))
                self.leUndo.setText(unicode(current.action.rev_id))
                tuottelias = True

            elif current.action.has_title() and self.rbRename.isChecked():
                self.koodinakyma2.setText(u"<i>[Uudelleennimetään: {} &rarr; {}]</i>".format(current.title(), current.action.title))
                self.leRename.setText(current.action.title)
                tuottelias = True

            self.current = current
                
        # Luodaan uusi sivu.
        else:
            print u"Luodaan uusi sivu"
            current = self.lista.get_new_page(title) 
            self.current = current
            if current.action.has_text():
                self.koodinakyma1.setText(u"<i>[Uusi sivu.]</i>")
                self.koodinakyma2.setText(unicode(current.action.text))
            else:
                self.koodinakyma1.setText(u"<i>[Uusi sivu.]</i>")
                self.koodinakyma2.setText(u"")

        if current.summary() != None:
            self.muutoskommentti.setEditText(current.summary())

        self.koodinakyma3.setText(self.koodinakyma2.toPlainText())
        self.koodi_muuttunut = 0

        if unicode(self.koodinakyma1.toPlainText()) == unicode(self.koodinakyma2.toPlainText()):
            self.merkitse_samaksi(True)
        
        # Katsotaan onko teksti muuttunut.
        if self.rbEditText.isChecked():
            if current.action.has_text() and alkup_wtxt:  

                if current.action.text == alkup_wtxt:
                    tuottelias = False
                else:
                    tuottelias = True
            # luotu uusi sivu
            elif current.action.has_text() and not alkup_wtxt:
                tuottelias = True
            else:
                tuottelias = False

        if tuottelias:
            print u"Oltiin tuottelias."
        else:
            print u"Ei oltu tuottelias."

        # Jos automaatti on päällä asetetaan tallennustimeri tai
        # siirrytään seuraavan, jos tekstille ei tehty mitään tai
        # sivua ei ollut.
        if self.is_automation_on() and current.paused == False: 
            if tuottelias:
                print u"timeri päälle"
                # yhdenkerran timeri
                QTimer.singleShot(1000, self.timerTallenna_triggered)
                print u"timeri päällä"
            else:
                QTimer.singleShot(1000, self.timerSeuraava_triggered)                

        elif current.paused == True:
            print u"Pause: {}".format(current.pause_info)
            self.automaatti.setChecked(False) 

        self.piilota_animaatio()

            
    @pyqtSignature("")
    def timerTallenna_triggered(self):
        """
        Sivun latauksen jälkeisen timerin laukeamisessa kutsuttava funktio. Lähettää koodin palvelimelle.
        """
        print u"timer: muka: Tallennetaan..."
        self.on_tallennus_clicked()
        
    @pyqtSignature("")
    def timerSeuraava_triggered(self):
        """
        Sivun latauksen jälkeisen timerin laukeamisessa kutsuttava funktio. Siirtyy seuraavaan.
        """
        print u"timer: Seuraava..."
        self.on_actionSeuraava_triggered()
    
    def sync_codes(self):
        """
        Kopioi koodinakyma2:n tekstin koodinakyma3:een tai toisin päin riippuen
        siitä kumpi on viimeksi muuttunut.
        """
        if self.koodi_muuttunut == 3:
            self.koodinakyma2.setText(self.koodinakyma3.toPlainText())
        elif self.koodi_muuttunut == 2:
            self.koodinakyma3.setText(self.koodinakyma2.toPlainText())
        self.koodi_muuttunut = 0
            
    @pyqtSignature("int")
    def on_tabWidget_currentChanged(self, index):
        """
        Välilehden vaihto.
        """
        # Päivitetään koodit samoiksi...
        self.sync_codes()
        # ... ja siirretään kursorit samoihin kohtiin
        if index == 0:
            # move cursor
            cur = self.koodinakyma2.textCursor()
            cur.setPosition(self.koodinakyma3.textCursor().position())
            self.koodinakyma2.setTextCursor(cur)
        elif index == 2:
            # move cursor
            cur = self.koodinakyma3.textCursor()
            cur.setPosition(self.koodinakyma2.textCursor().position())
            self.koodinakyma3.setTextCursor(cur)
        

    
    @pyqtSignature("")
    def on_koodinakyma2_textChanged(self):
        """
        Slot documentation goes here.
        """
        self.koodi_muuttunut = 2
        self.merkitse_samaksi(False)
        
    @pyqtSignature("")
    def on_koodinakyma3_textChanged(self):
        """
        Slot documentation goes here.
        """
        self.koodi_muuttunut = 3
        self.merkitse_samaksi(False)
    
    @pyqtSignature("")
    def on_actionLoadPreview_triggered(self):
        """
        Slot documentation goes here.
        """
        htmlkoodi = self.lista.get_api().get_preview(self.get_current_text(),  self.currentTitle)
        if htmlkoodi:
            self.sivunakyma3.setHtml(htmlkoodi, QUrl(u"http://fi.wiktionary.org/wiki/"))
    
    @pyqtSignature("QString")
    def on_tuloslista_currentItemChanged(self, current, previous):
        """
        Laukeaa kun tuloslistalta klikataan tai ohjelmallisesti valitaan alkio.
        """
        print "****************************************"
        if current:
            if current.background() == QBrush(QColor("cyan")): 
                print "ladataan lisaa"
                self.list_more()
            else:
                print "ladataan sivu"
                self.lataa_sivu(unicode(current.text()))
        else:
            print "Lista on tyhjä."

    def get_current_text(self):
        """
        Palauttaa tekstin jota on editoitu kummalla tahansa välilehdellä.

        @return (*py*string) editoitu (tai muuttamaton) teksti
        """
        # kopioidaan koodinäkymä 1:n ja 2:n koodit samoiksi
        self.sync_codes()
        return unicode(self.koodinakyma2.toPlainText()) # palauttaa qstringin
        
    def merkitse_samaksi(self, b):
        """
        Merkitsee editoitavat koodit samoiksi tai eriksi kuin serveriltä ladattu. Ei tarkista ovatko ne todella samoja.

        :param b (bool)    jos True merkitään samoiksi, False niin eriksi
        @return (None)
        """
        def update(w,  b):
            ob = w.property("sameAsOnServer")
            w.setProperty("sameAsOnServer", b)
            if b != ob:
                w.style().unpolish(w)  # päivittää värityksen
                w.style().polish(w)
                w.update()
        update(self.koodinakyma2, b)
        update(self.koodinakyma3, b)

        if self.currentRev == None:
            self.statusSama.setText("UUSI") 
        elif b == True: 
            self.statusSama.setText("SAMA") 
        else: 
            self.statusSama.setText("ERI") 

    @pyqtSignature("")
    def on_actionEdellinen_triggered(self):
        """
        Lataa listan edellisen sivun tai, jos ollaan ensimmäisessä, viimeisen.
        """
        i = self.tuloslista.currentRow()
        if i-1 >= 0:
            self.tuloslista.setCurrentRow(i-1)
        else: self.tuloslista.setCurrentRow(self.tuloslista.count()-1)
    
    @pyqtSignature("")
    def on_actionSeuraava_triggered(self):
        """
        Lataa listan seuraavan sivun tai, jos ollaan viimeisessä, ensimmäisen.
        """
        i = self.tuloslista.currentRow()
        if i+1 < self.tuloslista.count():
            self.tuloslista.setCurrentRow(i+1)
        else: 
            # Pysäytetään automaatti.
            if self.is_automation_on() == True:
                self.automaatti.setChecked(False)
            #self.tuloslista.setCurrentRow(0)

    @pyqtSignature("")
    def on_actionPoistaListasta_triggered(self):
        """
        Siirtää nykyisen sivun lykättyihin.
        """
        p = self.tuloslista.takeItem(self.tuloslista.currentRow())
        self.lista.siirra_lykattyihin(unicode(p.text()))
        
    @pyqtSignature("QEvent")
    def closeEvent(self, event):
        """
        Kutsutaan kun ohjelma suljetaan.
        """
        # tallennetaan summaryt
        self.save_summary_list()
        event.accept() # hyväksyy sulkemisen, vastakohta .reject()
